/**
 * @file         http_client_comp.c
 * @brief        
 * @note    
 * @author       lixinliang
 * @date         2020-08-25
 * @version      1.0
 *
 * @par          修改日志
 * <table>
 * <tr><th>Date        <th>Version               <th>Author          <th>Description
 * <tr><td>2020-08-25  <td>1.0                   <td>lixinliang      <td>创建初始版本
 * @copyright    杭州鸿泉物联网技术股份有限公司
 */

#ifndef _HTTP_CLIENT_COMP_H_
#define _HTTP_CLIENT_COMP_H_

#ifdef __cplusplus
extern "C" {
#endif

#include "ofc/ofc_types.h"
#include "mfc/mfc_serial_vir_bus.h"
#include "web_client/http_client_interface.h"
#include "web_client/webclient.h"

#if defined(__linux__)
#define POST_RESP_BUFSZ (1024 * 32 +1024)
#define POST_FILE_BUFSZ (1024 * 4)
#elif defined(CPU_S32K312) || defined(CPU_S32K314)
#define POST_RESP_BUFSZ (1024 * 2)
#define POST_FILE_BUFSZ (1460) // 最大可读字节数
#else
#define POST_RESP_BUFSZ (1024 * 8 +1024)
#define POST_FILE_BUFSZ (1024 *2)
#endif

#define HTTP_CLIENT_URL_LEN  128
#define HTTP_CLIENT_DATA_LEN 512

enum THttpClientStatusTag
{
    kHttpClientOk      = 0,
    kHttpClientError   = -1,
    kHttpClientTimeout = -2,
    kHttpClientNoMem   = -3,
    kHttpClientSocket  = -4,
    kHttpClientBuffer  = -5,    
    kHttpClientConnectFailed = -6,
    kHttpClientDisconnect = -7,
    kHttpClientFileNotExists = -8,
    kHttpClientPathError = -9,
    kHttpClientHttpsError = -10,
    kHttpClientServerError = -11,
    kHttpClientInvaildParam = -12,
    kHttpClientTerminal = -13,
};
typedef enum THttpClientStatusTag THttpClientStatus;

enum THttpClientEventIdTag
{
    kHttpClientEvError = 0,
    kHttpClientEvOnConnect ,
    kHttpClientEvOnDisconnect,
    kHttpClientEvOnFinish,
    kHttpClientEvOnHeader,
    kHttpClientEvOnData,
};
typedef enum THttpClientEventIdTag THttpClientEventId;

typedef struct THttpClientEventTag  THttpClientEvent;
struct THttpClientEventTag
{
    THttpClientEventId event_id;    ///< 事件
    Int32              status_code; ///< 状态码
    Int32              offset;      ///< 偏移地址，起始地址
    Int32              total_len;   ///< 下载:文件总大小        上传:本次数据回复的总大小
    void               *data;       ///< 回复的数据体 
    Int32              data_len;    ///< 下载:本次接收的数据大小 上传:本次数据回复的实际大小
};

typedef Int32 (*THttpClientDoDispacthEvent)(void *sender, THttpClientEvent *event);
typedef struct THttpClientOnDispacthEventTag THttpClientOnDispacthEvent;
struct THttpClientOnDispacthEventTag
{
    void *sender;                       ///< 事件对象
    THttpClientDoDispacthEvent Method;  ///< 事件方法
};

/**
 * HTTP Client 组件 
 */
typedef struct THttpClientCompTag THttpClientComp;
struct THttpClientCompTag
{
PRIVATE
    IHttpClientSocket        socket_if_;
    struct webclient_session web_session_;
    TMfSerialVirBus        *serial_vir_bus_;        ///< 虚拟串行总线对象

    UInt8     net_state_;
    Bool   is_active_;
    UInt16             recv_timeout_;///< 接收超时时间    
    UInt32    last_conn_tick_;
    char   host_[WEBCLIENT_HOST_LEN];
    char   uri_[WEBCLIENT_URL_LEN];
    UInt32 port_;
    Bool   *terminal_;       ///< 是否终止
    /// 静态缓存
    char  resp_buf[POST_RESP_BUFSZ];
PUBLIC
    THttpClientOnDispacthEvent dispatch_event; ///< 消息分发
};

/**
 * HTTP Client 创建
 * @param self: 对象
 * @param virbus: 虚拟总线 socket总线
 * @return
 */
void THttpClientCompCreate(THttpClientComp *self, TMfSerialVirBus *virbus, char *head_buf, Int32 head_len);

typedef struct THttpClientCompParamTag THttpClientCompParam;
struct THttpClientCompParamTag
{
    TMfSerialVirBus *virbus;
    char            *head_buf;
    Int32           head_len;
    Bool            *terminal;   ///< 是否终止
};

/**
 * HTTP Client 创建
 * @param self: 对象
 * @param virbus: 虚拟总线 socket总线
 * @return
 */
void THttpClientCompCreateEx(THttpClientComp *self, const THttpClientCompParam *param);

/**
 * HTTP Client 析构
 * @param self: 对象
 * @return
 */
void THttpClientCompDestroy(THttpClientComp *self);

/**
 * HTTP Client 设置接收超时时间
 * @param self: 对象
 * @param timeout: 接收超时时间 单位1ms，范围500ms-5S
 * @return
 */
Bool THttpClientCompSetRecvTimeOut(THttpClientComp *self, UInt16 timeout);

/**
 * HTTP Client 增加请求消息头字段
 * @param self: 对象
 * @param FORMAT: 头字段
 * @return -1:添加失败 >0: 添加成功，头字段的长度
 */

#define THttpClientHeaderFieldsAdd(http_ptr, fmt, ...) webclient_header_fields_add(&http_ptr->web_session_, fmt, ##__VA_ARGS__)

/**
 * HTTP Client 发送post消息
 * @param self: 对象
 * @param uri:  链接 比如 http://192.168.101.1:8080/register
 * @param post_data:  推送数据
 * @param data_len:  数据长度
 * @return <0:失败 >=0:http 状态码
 * @note 完成发送通知为 kHttpClientEvOnData 200 其他不通知
 */
Int32 THttpClientPost(THttpClientComp *self, const char *uri, const void *post_data, Int32 data_len);

/**
 * HTTP Client 消息获取[数据从0位置开始获取]
 * 从0位置开始，一直到最后，Chunk大小对端决定
 * @param self: 对象
 * @param uri:  链接 比如 http://192.168.101.1:8080/ota/filename.bin
 * @note 收到数据通知为 kHttpClientEvOnData 200 其他不通知
 * @return <0:失败 >=0:http 状态码
 */
Int32 THttpClientGet(THttpClientComp *self, const char *uri);

/**
 * HTTP Client 发送post消息
 * @param self: 对象
 * @param uri:  链接 比如 http://192.168.101.1:8080/register
 * @param post_data:  推送数据
 * @param data_len:  数据长度
 * @param header:  附加http请求头
 * @note 完成发送通知为 kHttpClientEvOnData 200 其他不通知
 * @return <0:失败 >=0:http 状态码
 */
Int32 THttpClientPostEx(THttpClientComp *self, const char *uri, const void *post_data, Int32 data_len, const char *header);

/**
 * HTTP Client 消息获取[数据从0位置开始获取]
 * 从0位置开始，一直到最后，Chunk大小对端决定
 * @param self: 对象
 * @param uri:  链接 比如 http://192.168.101.1:8080/ota/filename.bin
 * @param header:  附加http请求头
 * @note 收到数据通知为 kHttpClientEvOnData 200  其他不通知
 * @return <0:失败 >=0:http 状态码
 */
Int32 THttpClientGetEx(THttpClientComp *self, const char *uri, const char *header);

/**
 * HTTP Client 分段获取[常用作固件获取]
 * 从偏移位置开始，指定大小, 本端限制
 * @param self: 对象
 * @param uri:  链接 比如 http://192.168.101.1:8080/ota/filename.bin
 * @param offset:  数据偏移量(即起始地址)
 * @param size:  获取数据的大小
 * @return <0:失败 >=0:http状态码
 * @note 收到正确数据通知为 kHttpClientEvOnData 200  收到数据不完整或偏移不对通知为kHttpClientEvError 200，收到其他响应通知为 kHttpClientEvOnData 状态码
 */
Int32 THttpClientGetFileRange(THttpClientComp *self, const char *uri, Int32 offset, Int32 size);

typedef struct THttpClientGetFileRangeParamTag THttpClientGetFileRangeParam;
struct THttpClientGetFileRangeParamTag
{
    const char          *uri;           ///< 链接 比如 http://192.168.101.1:8080/ota
    Int32               offset;         ///< 偏移地址
    Int32               size;           ///< 获取数据的大小
    Bool  basic_authorization;          ///< 使用安全认证
    char  *user;                        ///< 用户名  
    char  *password;                    ///< 密码
};

/**
 * HTTP Client 分段获取[常用作固件获取]
 * 从偏移位置开始，指定大小, 本端限制
 * @param self: 对象
 * @param param: 参数
 * @return <0:失败 >=0:http状态码
 * @note 收到正确数据通知为 kHttpClientEvOnData 200  收到数据不完整或偏移不对通知为kHttpClientEvError 200，收到其他响应通知为 kHttpClientEvOnData 状态码
 */
Int32 THttpClientGetFileRangeEx(THttpClientComp *self, THttpClientGetFileRangeParam *param);

typedef Int32 (*THttpClientDoRead)(void *user_param, char *buffer, UInt32 size);

typedef struct THttpClientPostFileParamTag THttpClientPostFileParam;
struct THttpClientPostFileParamTag
{
    const char          *uri;           ///< 链接 比如 http://192.168.101.1:8080/ota
    const char          *form_data;     ///< 表单数据
    THttpClientDoRead   do_read;        ///< 读取回调函数
    void               *user_param;     ///< 读取回调函数参数
    UInt32              file_size;      ///< 文件大小
    UInt16              send_intv;      ///< 数据发送间隔时间  单位ms，范围20ms-2000ms

    Bool  basic_authorization;          ///< 使用安全认证
    char  *user;                        ///< 用户名  
    char  *password;                    ///< 密码
};

/**
 * post上传文件
 * @param self: 对象
 * @param param: 参数
 * @return <0:失败 >=0:http状态码
 * @note 完成发送通知为 kHttpClientEvOnData 200 其他不通知
 */
Int32 THttpClientPostFile(THttpClientComp *self, THttpClientPostFileParam *param, Bool *terminal);

void THttpClientRunOnce(THttpClientComp *self);

#ifdef __cplusplus
}
#endif

#endif // _HTTP_CLIENT_COMP_H_
